﻿use strict;
use utf8;
BEGIN {
	unshift @INC, "..";
}
use Unicode::Japanese;
use Win32::OLE qw(in with CP_UTF8);
use Win32::OLE::Const 'Microsoft Excel';

$Win32::OLE::CP = CP_UTF8;

my $TRUE = 1;
my $FALSE = 0;

my $excel;
my $tab_num;
my $show_content;
my ($row_num, $col_num);

main();
exit;

#-----------------------------------------------------------
# メイン。
#-----------------------------------------------------------
sub main {
	my @argv = @ARGV;
	my $list_file;

	# コマンドパラメータチェック。
	while (@argv and $argv[0] =~ s/^-(.*)$/$1/) {
		$_ = shift @argv;

		if (/^s$/) {
			$show_content = $TRUE;
		} else {
			usage();
			exit -1;
		}
	}

	if (@argv != 1) {
		usage();
		exit -1;
	}
	$list_file = $argv[0];

	$SIG{INT} = \&my_int;
	$excel = open_excel();

	open LIST, "$list_file" or my_error("error: open $list_file\n");
	while ($_ = get_line(\*LIST)) {
		my ($upper, $lower);
		my $type;

		if (/^D-/) {
			$type = "dir";
		} elsif (/^F-/) {
			$type = "file";
		} else {
			my_error("error: list format\n");
		}
		if (/-row +(\d+)/) {
			$row_num = $1;
		} else {
			$row_num = undef;
		}
		if (/-col +(\d+)/) {
			$col_num = $1;
		} else {
			$col_num = undef;
		}
		$upper = get_line(\*LIST);
		$lower = get_line(\*LIST);
		if (!$upper || !$lower) {
			my_error("error: list format\n");
		}
		chomp($upper, $lower);

		if ($type eq "dir") {
			if (!is_dir($upper)) {
				my_error("error: $upper is not dir\n");
			} elsif (!is_dir($lower)) {
				my_error("error: $lower is not dir\n");
			}
		} else {
			if (!is_file($upper)) {
				my_error("error: $upper is not file\n");
			} elsif (!is_file($lower)) {
				my_error("error: $lower is not file\n");
			}
		}
		if ($type eq "dir") {
			my (@up, %low, @common);

			for my $i (my_glob("$lower/*")) {
				if (is_file($i)) {
					if ($i =~ /([^\\\/]+)$/) {
						$low{$1} = $TRUE;
					}
				}
			}
			for my $i (my_glob("$upper/*")) {
				if ($i =~ /([^\\\/]+)$/) {
					my $file = $1;

					if ($low{$file}) {
						push @common, $file;
						delete $low{$file};
					} else {
						push @up, $file;
					}
				}
			}

			output("|$upper|$lower\n");
			tab_push();
			for my $i (@up) {
				output("U:$i\n");
			}
			for my $i (keys %low) {
				output("L:$i\n");
			}
			for my $i (@common) {
				my @diff;

				@diff = excel_diff("$upper/$i", "$lower/$i");
				if (@diff) {
					output("D|$i\n");
					tab_push();
					for my $i (@diff) {
						output("$i\n");
					}
					tab_pop();
				}
			}
			tab_pop();
		} else {
			my @diff;

			@diff = excel_diff($upper, $lower);
			if (@diff) {
				output("D|$upper|$lower\n");
				tab_push();
				for my $i (@diff) {
					output("$i\n");
				}
				tab_pop();
			}
		}
	}
	close LIST;
	$excel->Quit();
	$excel = undef;
}

#-----------------------------------------------------------
# 使用方法。
#-----------------------------------------------------------
sub usage {
	my $com = $0;
	$com =~ s/.*\\//;

	print STDERR "\n";
	print STDERR "USAGE: $com [OPTIONS] <list-file>\n";
	print STDERR "OPTIONS:\n";
	print STDERR "    -s       - show content\n";
}

#-----------------------------------------------------------
# ディレクトリか？
#-----------------------------------------------------------
sub is_dir {
	my ($dir) = @_;

	return -d Unicode::Japanese->new($dir)->sjis;
}

#-----------------------------------------------------------
# ファイルか？
#-----------------------------------------------------------
sub is_file {
	my ($file) = @_;

	return -f Unicode::Japanese->new($file)->sjis;
}

#-----------------------------------------------------------
# グロブ関数。
#-----------------------------------------------------------
sub my_glob {
	my ($path) = @_;
	my @list;

	@list = glob(Unicode::Japanese->new($path)->sjis);
	for my $i (@list) {
		$i = Unicode::Japanese->new($i, 'sjis')->getu;
	}
	return @list;
}

#-----------------------------------------------------------
# ファイルからの１行取得。
#-----------------------------------------------------------
sub get_line {
	my ($handle) = @_;
	my $line;

	$line = <$handle>;
	$line = Unicode::Japanese->new($line, 'sjis')->getu;
}

#-----------------------------------------------------------
# アウトプットタブの追加。
#-----------------------------------------------------------
sub tab_push {
	$tab_num++;
}

#-----------------------------------------------------------
# アウトプットタブの削除。
#-----------------------------------------------------------
sub tab_pop {
	$tab_num--;
}

#-----------------------------------------------------------
# アウトプット。
#-----------------------------------------------------------
sub output {
	my ($msg) = @_;

	print " " x ($tab_num * 4);
	print Unicode::Japanese->new($msg)->sjis;
}

#-----------------------------------------------------------
# エクセルファイルの比較。
#-----------------------------------------------------------
sub excel_diff {
	my ($upper, $lower) = @_;
	my ($u_data, $l_data);
	my ($u_row_num, $l_row_num, $u_col_num, $l_col_num) =
		($row_num, $row_num, $col_num, $col_num);
	my ($r_num, $c_num) = ($row_num, $col_num);
	my $s;
	my @diff;

	$upper = Unicode::Japanese->new($upper)->sjis;
	$lower = Unicode::Japanese->new($lower)->sjis;
	$u_data = get_data_from_excel($upper, \$u_row_num, \$u_col_num);
	$l_data = get_data_from_excel($lower, \$l_row_num, \$l_col_num);

	if (!defined($row_num)) {
		$s = $u_row_num - $l_row_num;
		$r_num = $u_row_num;
		if (0 < $s) {
			push @diff, "lowerが${l_row_num}行までしか無い";
			$r_num = $l_row_num;
		} elsif ($s < 0) {
			push @diff, "upperが${u_row_num}行までしか無い";
		}
	}

	if (!defined($col_num)) {
		$s = $u_col_num - $l_col_num;
		$c_num = $u_col_num;
		if (0 < $s) {
			push @diff, "lowerが" . num2col(${l_col_num}) . "列までしか無い";
			$c_num = $l_col_num;
		} elsif ($s < 0) {
			push @diff, "upperが" . num2col(${u_col_num}) . "列までしか無い";
		}
	}

	for my $i (0 .. ($r_num - 1)) {
		for my $j (0 .. ($c_num - 1)) {
			if ($u_data->[$i][$j] ne $l_data->[$i][$j]) {
				my $str;

				$str = $i + 1 . "行" . num2col($j + 1) . "列が異なる";
				if ($show_content) {
					$str .= ", U:$u_data->[$i][$j] L:$l_data->[$i][$j]";
				}
				push @diff, $str;
			}
		}
	}

	return @diff;
}

#-----------------------------------------------------------
# エラー出力。
#-----------------------------------------------------------
sub my_error {
	my ($msg) = @_;

	print STDERR Unicode::Japanese->new($msg)->sjis;
	exit -1;
}

#-----------------------------------------------------------
# エクセルオープン。
#-----------------------------------------------------------
sub open_excel {
	my $excel;

	$excel = Win32::OLE->new('Excel.Application');
	if (!defined($excel)) {
		die "error: start Excel\n";
	}
	return $excel;
}

#-----------------------------------------------------------
# エクセルブック取得。
#-----------------------------------------------------------
sub get_book {
	my ($excel, $file_name) = @_;
	my $book;

	$book = $excel->Workbooks->Open($file_name);
	if (!defined($book)) {
		die "error: open $file_name\n";
	}
	return $book;
}

#-----------------------------------------------------------
# エクセルシート取得。
#-----------------------------------------------------------
sub get_sheet {
	my ($book, $sheet_num) = @_;
	my $sheet;

	$sheet = $book->Worksheets($sheet_num);
	if (!defined($sheet)) {
		die "error: get sheet $sheet_num\n";
	}
	return $sheet;
}

#-----------------------------------------------------------
# エクセルからデータ取得。
#-----------------------------------------------------------
sub get_data_from_excel {
	my ($book_name, $r_num, $c_num) = @_;
	my ($book, $sheet, $range, $data);
	my ($end_row, $end_col);

	$book = get_book($excel, $book_name);
	$sheet = get_sheet($book, 1);
	$range = $sheet->Range("A65536")->End(xlUp);
	if (defined(${$r_num})) {
		$end_row = ${$r_num};
	} else {
		$end_row = $range->{Row};
		${$r_num} = $end_row;
	}
	$range = $sheet->Range("IV1")->End(xlToLeft);
	if (defined(${$c_num})) {
		$end_col = ${$c_num};
	} else {
		$end_col = $range->{Column};
		${$c_num} = $end_col;
	}
	$end_col = num2col($end_col);
	$data = $sheet->Range("A1:$end_col$end_row")->{Value};
	$book->Close();

	return $data;
}

#-----------------------------------------------------------
# エクセルのカラム数を文字列に変換。
#-----------------------------------------------------------
sub num2col {
	my ($num) = @_;
	my $col = "A";

	for my $i (2 .. $num) {
		$col++;
	}
	return $col;
}

#-----------------------------------------------------------
# 中止。
#-----------------------------------------------------------
sub my_int {
	if (defined($excel)) {
		$excel->Quit();
		$excel = undef;
	}
	exit 1;
}

#-----------------------------------------------------------
# 終了時処理。
#-----------------------------------------------------------
sub END {
}

__END__
